Unit graphics;

{some graphic function for a simple arcade game}

Interface

Uses smallcrt, dos, GrafData;

Const
  VGAx = 320;
  VGAy = 200;
  VgaSeg = $A000; {Offset to VGA memory segment}

  { color enumeration }
  cl_ShadowBlack = 1; { black used for shadow, not zero (0) }
  cl_DRed   = 129;
  cl_DGreen = 177;
  cl_DBlue  = 79;
  cl_DPurple= 168;

  cl_DRed2  = 126; { or 125}
  cl_DGreen2= 122;
  cl_DBlue2 = 70;
  cl_DPurple2=149; {darker}

{  cl_LRed   = ?;}
  cl_LGreen = 222;
  cl_LBlue  = 230;
  cl_LPurple= 229; {lighter}


  cl_Green = 182;
  cl_Blue  = 110;
  cl_Red   = 133;
  cl_Purple= 175;

  cl_DGrey = 153;
  cl_Grey  = 216;
  cl_White = 235;

{ ---------------------------------------------------- }
{ -- the snake 'sprite' colors per difficulty level -- }
{ ---------------------------------------------------- }
const
  SnakeArray : Array[0..15] of Byte =
  (
    cl_Green, cl_White, cl_Green, cl_DGreen,{ easy, green snake }
    cl_Blue, cl_LBlue,  cl_Blue,  cl_DBlue,  { normal, blue snake }
    cl_Red, cl_White, cl_Red, cl_DRed,        { hard, red snake }
    cl_Purple, cl_LPurple, cl_Purple, cl_DPurple  { mission impossible, hidden difficulty, purple }
  );

  { colors of obstacle per level }
  ObstacleArray : Array[1..11] of Byte =
  (
    177, 181, 195, 116, 210, 104, 173, 167, 131, 135, 135
  ); {value 11th is for if obstacle on level border at end of game }


type
  TVirtScr    = array[0..VGAx*VGAy] of byte; {screen res. 320*200}
  TVirtPntr   = ^TVirtScr;                 {pointer to memory array}

  TPalRecord = Record
    Red   : Byte;
    Green : Byte;
    Blue  : Byte;
  end; {record}

var
  VirtScr : ^TVirtScr;
  VirtSeg :  word;                       {virtual screen offset}

  FontShadow : byte; {color of font shadow, 0=no shadow}


  SaveExitProc : Pointer; {pointer to old exitproc}

Procedure Screenmode(Mode: word);
Procedure WaitForRetrace;
Procedure SetColor (color, red, green, blue : byte);
Procedure GetColor(color : byte; var red, green, blue : byte);

Procedure CycleColors(StartColor, HowMany : Byte);

{ palette routines }
Procedure SetPalette;

{basic graphics routines}
Procedure HorizontalLine(x1,x2,y : integer ; color : Byte);
Procedure DrawBox(Xpos, Ypos, xsize, ysize : Word; Color : Byte);
Procedure DrawBox_old(x1, y1, x2, y2 : Word; Color : Byte);

Procedure HScrollScreen(Dir : Boolean);

{ for virtual screen }
Procedure V_Flip;                              { For the  }
Procedure V_Cls;                               { screen.  }

{procedures for text writing}
procedure WriteText(X, Y: Byte; S: String; Color: Byte);
procedure WriteTextBlink(X, Y: Byte; S: String);

{basics for text writing}
Procedure SetFontShadow(ShadowColor : byte);
Procedure PutLetter(xPos, yPos : integer; Letter, Color : byte);

{procedure for the look of the cave}
procedure PutCaveSlice(xPos : Word);
{procedure PutCaveSlice_asm(xPos : Word);}
procedure PutSnakeSlice(xPos : Word; yPos : Byte);

{ procedures to put logo and skull sprites }
procedure PutLogoSprite;
procedure PutSkull(YPos : Byte);

{ procedures for hidden scroll text }
procedure InitScrollText;
procedure DisplayScrollText;

procedure InitGraphics(Level : Byte);

Implementation

uses
  gamecode;

var
  ScrollLetter, ScrollShift : Byte;
  ScrollCursor : Word;
  CaveSliceCounter : Word;
  CaveSliceOffset : Word;

Procedure Screenmode(Mode : word);
{ 13h = Vga 320x200, 256 colors }
{ 03h = text 80x24, color }
Begin
  Asm
    Mov  AX,Mode
    Int  10h
  End;
End;

procedure WaitForRetrace; assembler;   { Wait for a vertical retrace. }
asm
        Mov  DX,3DAh;
@WAIT1: In   AL,DX;
        Test AL,8;
        Jz   @WAIT1
@RETR2: In   AL,DX;
        Test AL,8;
        Jnz  @RETR2;
end;

procedure SetColor(Color, Red, Green, Blue : Byte); assembler; { Setcolor }
{
  equevalent Turbo Pascal code is like this :
  Begin
    Port[$3C8] := Color;
    Port[$3C9] := Red;
    Port[$3C9] := Green;
    Port[$3C9] := Blue;
  End;
}
asm
  push ds

  mov     dx, 03C8h       { DAC Write Address Register }
  mov     al, Color
  out     dx, al
  inc     dx
  mov     al, Red
  out     dx, al
  mov     al, Green
  out     dx, al
  mov     al, Blue
  out     dx, al

  pop ds
end;


Procedure GetColor(Color : Byte; var Red, Green, Blue : Byte); { Getcolor }
Begin
   Port[$3c7] := Color;    { This procedure reads the values of    }
   Red   := Port[$3c9];    { Red, Green & Blue for a certain color }
   Green := Port[$3c9];    { from the [$3c9] port.                 }
   Blue  := Port[$3c9];
End;

(*
procedure GetColor(color : Byte ; var red, green, blue : Byte); assembler;
asm
  push   ds

  mov    dx, 3C7h
  mov    al, color
  out    dx, al
  inc    dx
  inc    dx                 { dx=3C9h }
  in     al, dx
  mov    byte [red], al
  in     al, dx
  mov    byte [green], al
  in     al, dx
  mov    byte [blue], al

  pop    ds
end;
*)

Procedure CycleColors(StartColor, HowMany : Byte);
var
  i, r, g, b, r2, g2, b2  : Byte;
Begin
  { save last color }
  GetColor((StartColor + HowMany), r2, g2, b2);
  { shift all colors to one place lower }
  for i := (StartColor + HowMany - 1) downto StartColor do
  begin
    GetColor(i, r, g, b);
    SetColor(i+1, r, g, b);
  end;
  { set first color=last color }
  SetColor(StartColor, r2, g2, b2);
End;


Procedure SetPalette;
var
  i, r, g, b : Byte;
Begin
  for i := 0 to 255 do
  begin
    r := PaletteArray[i][0];
    g := PaletteArray[i][1];
    b := PaletteArray[i][2];
    SetColor(i, r, g, b);
  end;

  for i := 1 to 20 do
  begin
{    r := (Palette[PalSet, i].Red shr 2);
    g := (Palette[PalSet, i].Green shr 2);
    b := (Palette[PalSet, i].Blue shr 2);}
    SetColor(i+235, i, i, i);
  end;

End;

Procedure InitGraphics(Level : Byte);
begin
  CaveSliceOffset := (Level-1)*256;
  CaveSliceCounter := 0;
end;


Procedure HorizontalLine(x1,x2,y : integer ; color : Byte);
var
  i : word;
Begin
for i := x1 to x2 do
  mem[VirtSeg:y*VGAx+i] := Color;
end;

Procedure DrawBox_old(x1, y1, x2, y2 : Word; Color : Byte);
{draw a filled box of <Color>}
var
  i, j : word;
begin
  for i := y1 to y2 do
    for j := x1 to x2 do
      mem[VirtSeg:i*VGAx+j] := Color;
end;

Procedure DrawBox(Xpos, Ypos, xsize, ysize : Word; Color : Byte);
Begin
  Asm
  push  ds
{  push  si
  push  es
  push  di}

  {if you FIRST set DS and then ES it crashes with a 216-error.. weird}
  mov   ax, VirtSeg {destination}
  mov   es, ax      {destination is segment ES, offset DI (ES:DI)}

  mov   ax, VGAx
  mov   cx, [Ypos]
  mul   cx {MULtiply AL by source byte or AX by source word (unsigned)}
  add   ax, Xpos

  mov   di, ax { di is offset }

  mov   bx, VGAx
  sub   bx, [Xsize]

  mov   al, [Color]

  cld
  { cx works as a counter }
  mov   cx, [Ysize] { how many pixels on a vertical row }
@@next_row:
    push cx
    { cx works as a counter }
    mov   cx, [Xsize] { how many pixels on a vertical row }
    rep stosb
    add di, bx

    pop cx
    loop @@next_row

  { finished }
{  pop   di
  pop   es
  pop   si}

  pop   ds
  end;{end asm}
end;

procedure V_Flip; Assembler;    { Flip virtual screen on normal screen }
{ this procedure was copied from Vulture of Outlaw Triad (1996) }
Asm
  push    ds                { Put DS on stack }
  cld
  mov     ax,segA000         { Destination segment }
  mov     es,ax             { es points to destination segment }
  mov     ax,VirtSeg        { Source segment }
  mov     ds,ax             { ds points to source segment }
  xor     si,si             { Start at begin of source }
  xor     di,di             { Start at begin of destination }
  mov     cx,16000          { Screen size = 64 kbytes. Use dwords: needed 16000 }
  db      $66               { Use extended registers for speed (e.g eax) }
  rep     movsw             { Copy ds:si to es:di }

  pop     ds                { Recall DS from stack }
End;
{begin
  Move(mem[VirtSeg:0],mem[VgaSeg:0],VGAx*VGAy);
end;}

procedure V_Cls; Assembler;             { Clear virtual screen }
{ this procedure was copied from Vulture of Outlaw Triad (1996) }
Asm
  push ds                   { Put DS on stack }
  mov  ax,VirtSeg           { Move destination in AX }
  mov  es,ax                { ES points to VGA or virtual screen }
  xor  di,di                { Start at begin of virtual screen }
  cld                       { Clear direction flag }
{  mov  al,clrBlack          { Set color in AL }
{  mov  ah,al                { And in ah }

{next is a fix by jeff leyda}
        mov     al, 0        { color}
        mov     ah, al          { put it into ah too}
        push    ax                      { save it}
        mov     cx, 16          { set up shift counter}
        db      $66                     { 32bit instruction}
        shl     ax, cl          { shift 15:0 to 31:16}
        pop     ax                      { restore original value}
{end fix}

  mov  cx,320*50            { Do the entire screen (using dwords!) }
  db   $66                  { We want to use dwords }
  rep  stosw                { Store the word in ax to es:[di] }
  pop  ds                   { Recall DS from stack }
End;

procedure WriteText(X, Y: Byte; S: String; Color: Byte);
{print a text string to the virtual screen}
var
  i, nrC, YPos : Byte;
  C: Char;
  XPos : Word;
begin
  { simple 25*40 raster }
  XPos := (X * 8);
  YPos := (Y * 8);

  i := 1;
  while i <= Length (S) do
  begin
    C := S [i];
    nrC := Ord(C)-32;
    if (FontShadow <> 0) then PutLetter(XPos+1, YPos+1, nrC, FontShadow); {put shadow letter first}
    PutLetter(XPos, YPos, nrC, Color);  {put letter on virtual screen}
    XPos := XPos + 8;
    Inc (i); {move to next letter in string}
  end;
end;

procedure WriteTextBlink(X, Y: Byte; S: String);
var
  i, j, Color, Color1 : Byte;
begin
  { determine color according to difficultyr: green, blue or red }
  Color1 := SnakeArray[Difficulty * 4];

  for i := 1 to 20 do { 20 frames is 1/3 a second }
  begin
    if ((i mod 2) = 0) then
      Color := cl_White
    else
      Color := Color1;

    WriteText(X, Y, S, Color);
    V_Flip;
    for j := 1 to 2 do
      WaitForRetrace;
  end;
end;

procedure SetFontShadow(ShadowColor : byte);
begin
  FontShadow := ShadowColor;
end;

(*
procedure PutLetter(xPos, yPos : integer; Letter, Color : byte);
{ simple font routine }
var
  Width, Heigth, WidthClipped, NotShown : byte;
  FontDataPtr : Pointer;
begin

  for Heigth := 0 to 7 do
    for Width := 0 to 7 do
      if (FontArray[Letter][Heigth] and (128 shr Width) <> 0 ) then
        mem[VirtSeg:((yPos + Heigth) * VGAx) + xPos + Width] := Color;

end;
*)

procedure PutLetter(xPos, yPos : integer; Letter, Color : byte);
{test routine, aim is to get it working in ASM ( = faster)}
var
  Width, Heigth : byte;
  HelperPointer : Pointer;
begin
  {incase of illegal letter, make it 127 (a square) }
  if (Letter > c_NrOfLetters) then Letter := (127-31);

  HelperPointer := @FontData;{Font[Letter].FontData;}

  asm
  push  ds

  mov   ax, VirtSeg {destination}
  mov   es, ax      {destination is segment ES, offset DI (ES:DI)}

{  FontData segement {source is segment DS, offset SI (DS:SI)}
  lds si, [HelperPointer]

  {read width out of letter memory}
  mov   ax, 0
  mov   al, [Letter]
  mov   cx, 8
  mul   cx
  add   si, ax

  {prepare AX to be the offset in video memory}
  mov   ax, VGAx
  mov   cx, ypos
  mul   cx {MULtiply AL by source byte or AX by source word (unsigned)}
  add   ax, xpos {AX is now 320*ypos+xpos, this is the videomemory offset}

  cld
  mov   dl,0 {dl will keep track of which letter row we are writing}

@@write_row:
{  mov   si, cx {source offset}
    mov   di, ax  {destination offset}

    mov   dh, byte ptr [si] {get next one byte to write}
    inc   si

    {prepare cl to be width of letter (clipped if neccesary)}
    mov   cl, 8 {nr of pixels in this row that need to be put on screen}
{    cld                {CLear Direction flag. BDR: what is this for?}
@@write_pixel:
      rcl  dh,1        {Rotate destination byte through Carry Left by 1 bit}
      jc   @@pixel_notzero   {if carry is set(=1), print it to screen}
@@NoPaint:

{      push ax {four lines to test, every letter has background color red}
{      mov al, 4
{      stosb                 {move to screen, DI & SI automatically increased}
{      pop ax}

      inc     di            {only increase the register DI (screen offset) }
      loop    @@write_pixel
      jmp     @@next_line
@@pixel_notzero:
      push    ax
      mov     al, Color
      stosb                 {move to screen, DI & SI automatically increased}
      pop ax
      loop  @@write_pixel
@@next_line:
    add   ax, VGAx {go to next row}

    inc   dl       {INCrement dl by 1}
    cmp   dl, 8 {last letterrow reached?}
    jne   @@write_row {jump if not equal}
  jmp @@finished

@@finished:
  pop   ds {BdR: why is this needed!?!? it crashes without this..}
  end;{end asm}
end;

procedure PutLetterSlice(Letter, bShift : byte);
{ test routine, aim is to get it working in ASM ( = faster) }
var
  Width, Heigth : byte;
  HelperPointer : Pointer;
begin
  HelperPointer := @FontData;{Font[Letter].FontData;}

  asm
  push  ds
  mov   ax, VirtSeg {destination}
  mov   es, ax      {destination is segment ES, offset DI (ES:DI)}
  mov   di, (VGAx-1) {start in top-righthand crner of screen}

{  FontData segement {source is segment DS, offset SI (DS:SI)}
  lds   si, [HelperPointer]

  {read width out of letter memory}
  mov   ax, 0
  mov   al, [Letter]
  mov   cx, 8
  mul   cx
  add   si, ax

  mov   dl,0 {dl will keep track of which letter row we are writing}

{  cld
  {prepare cl to be height of letter }
  mov   cx, 0
  mov   cl, 8 {nr of pixels }

@@write_column:
{  mov   si, cx {source offset}
{    mov   di, ax  {destination offset}
    push  cx
    add     di, (VGAx-1)     {increase the register DI (screen offset) }

    mov   dh, byte ptr [si] {get next one byte to write}
    inc   si

    and   dh, [bshift]
{    cmp   dh, 0}

    jz    @@pixel_zero   {if carry is set(=1), print it to screen}

    push    ax
    mov     al, cl_white
    stosb                 {move to screen, DI & SI automatically increased}
    pop ax
    jmp @@pixel_notzero

@@pixel_zero:
    inc   di
@@pixel_notzero:

    pop   cx
    loop  @@write_column

@@finished:

{  mov al, [ScrollShift]
  shl al, 1
  mov [ScrollShift], al}

  pop   ds {BdR: why is this needed!?!? it crashes without this..}
  end;{end asm}
end;

procedure InitScrollText;
begin
  ScrollShift  := 128; { 1000 0000 b  }
  ScrollCursor := 0;
  ScrollLetter := ScrollTextArray[ScrollCursor];
end;

procedure DisplayScrollText;
begin
  PutLetterSlice(ScrollLetter, ScrollShift);
  If (ScrollShift = 1) then
  begin
    { reset scrollshift to 10000000b }
    ScrollShift := 128;
    Inc(ScrollCursor);
    if (ScrollCursor > c_ScrollLength) then ScrollCursor := 0;
    ScrollLetter := ScrollTextArray[ScrollCursor];
  end
  else
    { shift the '1' bit one to left, example 00100000b -> 00010000b }
    ScrollShift := ScrollShift div 2;
end;

(*
this pascal procedure was optimized to assembler procedure
Procedure HScrollScreen;
{ scroll screen between vertical lines Y1 to Y2
  up (Dir=true) or down (Dir=false)
  by (Dist) pixels

  Y1 must be smaller than Y2 ! }
var
  i, j, SrcOffset, DesOffset, ChunkToMove : Word;
  tmpPixel : Byte;
begin
  for i := 0 to (VGAy-8-1) do {except bottom line with 'score:' and 'level:' }
    for j := 1 to (VGAx) do
    begin
      tmpPixel := mem[VirtSeg:(i*VGAx)+j];
      mem[VirtSeg:(i*VGAx)+j-1] := tmpPixel;
{      mem[VirtSeg:(i*VGAx)+j-1] := 0;}
    end;
end;

*)


Procedure HScrollScreen(Dir : Boolean);
{-------------------------------}
{ parameter Dir = True  means scroll to the left }
{ parameter Dir = False means scroll to the left }
var
  SrcOffset, DesOffset : Word;
begin
  asm

  push  ds

  { if (Dir = true) jump then @@__moveright}
  cmp    byte ptr [Dir],01 { True = 0, False = 1}
  jne    @@__moveright

  { prepare for moving screen to left }
  cld    {clear direction flag }
  xor    ax, ax
  mov    [DesOffset],ax { at start of screensegment to move }
  inc    ax
  mov    [SrcOffset],ax { one byte after start of screensegment }

  jmp    @@__preparemove { skip prepare-move-right part }

  { prepare for moving screen to right }
@@__moveright:
  std    { set direction flag }
  mov    ax, (VGAx*(VGAy-9)-1)  { move entire screen, except bottom 9 lines }
  mov    [DesOffset],ax { DesOffset at the end of screensegment to move }
  dec    ax             { SrcOffset one byte before the end }
  mov    [SrcOffset],ax {SrcOffset = bp-02}

@@__preparemove:
  { set the source and destination according to move-left or move-right }
  mov    ax, [VirtSeg]   { virtual screen segment }

  mov    es, ax          { prepare destination }
  mov    di, [DesOffset] { prepare destination }

  mov    ds, ax          { prepare source (same segment, different offset) }
  mov    si, [SrcOffset] { prepare source }

  { make cx the number of bytes that need to be moved, for REP statement }
  mov    cx, (VGAx*(VGAy-9)-1) { entire screen, except bottom 9 lines }

  { rep movsb moves segment 1 byte left if direction flag is set }
  { rep movsb moves segment 1 byte left if direction flag is not set }
  rep    movsb { repeat move string byte }

  pop    ds
  end; {asm}
end;

(*
procedure PutCaveSlice(xPos : Word);
var
  i, j : Byte;
  VirtsegOffset, CaveSliceOffset2 : Word;

begin
  j := 0;

  { calculate one time here, instead of inside 'for i' loop (VGAy-1) times }
  VirtsegOffset := xPos;
  CaveSliceOffset2 := CaveSliceCounter*16;

  for i := 0 to VGAy-8 do
  begin
    mem[VirtSeg:VirtsegOffset] := PatternArray[CaveSliceOffset + CaveSliceOffset2 + j];
    VirtsegOffset := VirtsegOffset + VGAx; { next line }
    j := (j + 1) mod 16;
  end;
  CaveSliceCounter := (CaveSliceCounter + 1) mod 16;
end;
*)

{
optimized code to asm, but at first couldn't figure out
how to load the pointer to a procedure
example
procedure BitmapData; assembler
asm
  db  194,197,197,197,197,197,197,197,184,200,197,197,197,197,197,197
end;
and then
  lds si, @BitmapData { <- it crashed on this line }

procedure PutCaveSlice(xPos : Word);
var
  HelperPointer : Pointer;
  HelperWord : Word;
begin
  HelperPointer := @PatternData; {when put in the assembler part it does not go right }
{  HelperByte := GameCode.Level; {when put in the assembler part it does not go right }
  HelperWord := CaveSliceOffset + CaveSliceCounter;
  CaveSliceCounter := (CaveSliceCounter + 16) mod 256;
  asm

  push  ds

  { set the destination }
  mov    ax, [VirtSeg]   { virtual screen segment }
  mov    es, ax          { prepare destination }

  mov    ax, [xPos]
  mov    di, ax     { prepare destination }

  { prepare source, pattern data segment }
  lds   si, pointer [HelperPointer] { <- must be done like this.. don't know why.. }

  { jump into the datasegment to pattern for current level }
  mov   ax, [HelperWord]
{  mov   al, [HelperByte] { <- WHAT THE FUCK!!! if i put GameCode.Level here it does not go right}
{  dec   al
  mov   cx, 256 { pattern is 16*16 pixels }
{  mul   cx { MULtiply AX by source word (unsigned) result in ax }

  { CaveSliceCounter contains the 'offset' within the pattern }
{  add   ax, word ptr [CaveSliceCounter]}
  mov   si, ax

  { save offset into pattern segment }
{  push  ax

  { bl acts as helpcounter }
  xor    bl, bl

  { restore this offset into pattern segment }
{  pop    ax}
  cld
  { the loop }
  mov    cx, (VGAy-9)
{  mov    ax, 150 {test, color}
@@__patternloop:

{    stosb}
    movsb       {test}

    add    di, (VGAx-1) { skip to next line of screen }

    inc    bl
    cmp    bl, 16
    jne    @@__patternnoreset
{      inc    ax   { test }
      xor    bl, bl { reset helper counter to 0 }
      sub    si, 16      { reset si to initial offset into pattern segment }
    @@__patternnoreset:
    loop   @@__patternloop

  { CaveSliceCounter := (CaveSliceCounter + 16) mod 256 }
{  add    Word ptr [CaveSliceCounter], 16
  and    Word ptr [CaveSliceCounter], 240{AND with 1111 0000b }

  pop   ds
  end;
end;


procedure PutSnakeSlice(xPos : Word; yPos : Byte);
var
  tmpByte : Byte;
  Offset : Word;
begin
  tmpByte := Difficulty * 4;

  Offset := (VGAx*yPos)+xPos;

  mem[VirtSeg:Offset + (VGAx*0)] := SnakeArray[tmpByte+0];
  mem[VirtSeg:Offset + (VGAx*1)] := SnakeArray[tmpByte+1];
  mem[VirtSeg:Offset + (VGAx*2)] := SnakeArray[tmpByte+2];
  mem[VirtSeg:Offset + (VGAx*3)] := SnakeArray[tmpByte+3];
end;

procedure PutLogoSprite;
const
  xSize = 297;
  ySize = 51;
var
  x, y : Word;
  Pixel : Byte;
begin
  for y := 0 to ySize-1 do
    for x := 0 to xSize-1 do
    begin
      Pixel := LogoArray[(y*xSize)+x];
      mem[VirtSeg:((4+y)*VGAx)+(10+x)] := Pixel;
    end;
end;

procedure PutSkull(YPos : Byte);
const
  xSize = 23;
  ySize = 14;
var
  x : Word;
  y, Pixel : Byte;
begin
  { adjust, snakeheigth=4, skull height=14 -> (14 - 4) / 2 }
  YPos := YPos - 5;

  for y := 0 to ySize-1 do
    for x := 0 to xSize-1 do
    begin
      Pixel := SkullArray[(y*xSize)+x];
      if (Pixel <> 0) then { color 0 is transparancy }
        mem[VirtSeg:((YPos+y)*VGAx)+(PlayerXpos-11+x)] := Pixel;
    end;

end;

procedure GrafxExitProc; Far;
var
  i: byte;
  MemSize: Word;
begin
  ExitProc := SaveExitProc;

  {free allocated memory }
  Freemem(VirtScr, VGAx*VGAy); {virtual screen memory}

{  for i:= 1 to NrOfLetters do {all font memory}
{    FreeMem(Font[i].FontData, LetterHeigth);

  for i:= 1 to NrOfSprites do {all sprite memory}
{  begin
    MemSize := Sprite[i].Xsize * Sprite[i].Ysize;
    FreeMem(Sprite[i].SpriteData, MemSize);
  end; {for}
end; {GrafxExitProc}

begin {unit initialisation}
  { debug }
{  writeln('initialise graphics');}
  {save exitproc and set it to }
  SaveExitProc := ExitProc;
  ExitProc := @GrafxExitProc;

  Getmem(VirtScr, VGAx*VGAy); { Get memory for virtual screen }
  VirtSeg := seg(VirtScr^);

  FontShadow := 0; {no shadow=0}
end.  {nothing.. yet}
